package main

//测试脚本
/*
	测试客户端，使用方法：
	1. 如果是压力测试，需要先在db中创建startId-endId之间的账户并写入token，然后打开benchmark开关;
	2. 普通测试只需要修改下面的userId和token即可；
	3. 修改serverHost到制定目标环境；
	4. 使用go run test_client.go进行测试

	注意：本客户端并不能完全模拟客户端行为，请根据情况自行修改。
*/
import (
	"flag"
	"fmt"
	"lol.com/feb214-game/nest.git/leaf"
	"lol.com/feb214-game/nest.git/proto"
	"lol.com/feb214-game/nest.git/tools/sample"
	"lol.com/feb214-game/nest.git/tools/tz"
	"math/rand"
	"net/url"
	"os"
	"os/signal"
	"sync"
	"talent.com/cube/conf/g"
	"time"

	"github.com/gorilla/websocket"
	"talent.com/cube/common/msg"
)

var (
	allConn = &sync.Map{}
)

type Conn struct {
	sync.Mutex
	ID       uint64
	RoomKind int32
	GameType int32
	RealConn interface{}
}

type ClientInfo struct {
	gameType int32
}

const (
	userId = 60011253151
	token  = "6ce3a74ba23d4facb6b250a995e8a7d2"
	//serverHost = "127.0.0.1"
	serverHost = "13.127.185.133"
	wsAddr     = serverHost + ":8017"
	startId    = uint64(3794)
	endId      = uint64(3990)
	benchmark  = true
	gameType   = g.Jmg
	betIndex   = 1
)

func wsMsgHandler(close chan struct{}, wg *sync.WaitGroup, wsconn *websocket.Conn) {
	wg.Add(1)
	defer wg.Done()
	for {
		select {
		case <-close:
			fmt.Println("closing websocket")
			_ = wsconn.Close()

			return
		default:
			_, data, err := wsconn.ReadMessage()
			if err != nil {
				fmt.Printf("!!!ws error: %v", err)
				_ = wsconn.Close()
				return
			} else {
				handlePush(data, wsconn)
			}
		}
		time.Sleep(20 * time.Millisecond)
	}
}

func handlePush(data []byte, conn interface{}) {
	m, err := leaf.MsgProcessor.Unmarshal(data)
	c, _ := allConn.Load(conn)
	realConn := c.(*Conn)
	if err != nil {
		fmt.Println(err)
	} else {
		switch m.(type) {
		case *msg.RoomST:
			r := m.(*msg.RoomST)
			if !benchmark {
				fmt.Printf("%+v\n", r)
			}
		case *msg.StartBetNT:
			nt := m.(*msg.StartBetNT)
			count := sample.RandInt(1, 10)
			count = 2
			for i := 1; i < count; i++ {
				betIdx := int32(rand.Intn(2)) + 1
				betIdx = 0

				sendMsg(&msg.BetReq{
					Timestamp:  tz.GetNowTsMs(),
					TermNumber: nt.TermNumber,
					Info: map[int32]*msg.ChipInfo{
						betIdx: {
							Data: map[int64]int32{
								1000: 1,
							},
						},
					},
				}, realConn)
				time.Sleep(50 * time.Millisecond)
			}
		case *msg.AwardNT:
			nt := m.(*msg.AwardNT)
			fmt.Printf("=======>award info:%+v", nt)
		case *proto.ErrorST:
			x := m.(*proto.ErrorST)
			fmt.Printf("error happened!!!, status:%v, msg:%s\n", x.Status, x.Msg)
		default:
			if !benchmark {
				fmt.Printf("received %+v\n", m)
			}
		}
	}
}

var addr = flag.String("addr", wsAddr, "http service address")

func getWSconn(id uint64) (*websocket.Conn, *Conn) {
	u := url.URL{Scheme: "ws", Host: *addr, Path: "/ws"}
	var dialer websocket.Dialer
	wsconn, _, err := dialer.Dial(u.String(), nil)
	if err != nil {
		panic(err)
	}
	c := &Conn{
		ID:       id,
		RealConn: wsconn,
	}
	allConn.Store(wsconn, c)
	return wsconn, c
}

func initWSConn(cliInfo *ClientInfo) *websocket.Conn {
	wsconn, c := getWSconn(userId)
	sendMsg(&msg.AuthReq{
		Timestamp: tz.GetNowTsMs(),
		UserID:    userId,
		Token:     token,
		GameType:  cliInfo.gameType,
		RoomKind:  0,
		Version:   511,
	}, c)
	return wsconn
}

func initWSConns(wg *sync.WaitGroup, closeChan chan struct{}) {
	for i := startId; i <= endId; i++ {
		time.Sleep(time.Duration(sample.RandInt(20, 50)) * time.Millisecond)
		wsconn, c := getWSconn(i)
		sendMsg(&msg.AuthReq{
			Timestamp: tz.GetNowTsMs(),
			UserID:    i,
			Token:     token,
			GameType:  gameType,
			RoomKind:  0,
			Version:   511,
		}, c)
		go wsMsgHandler(closeChan, wg, wsconn)
		fmt.Printf("User%v connected\n", i)
	}
}

func sendMsg(msg interface{}, conn *Conn) {
	conn.Lock()
	defer conn.Unlock()
	c, _ := conn.RealConn.(*websocket.Conn)
	sendWSProtoMsg(msg, c)
}

func sendWSProtoMsg(msg interface{}, wsconn *websocket.Conn) {
	data, _ := leaf.MsgProcessor.Marshal(msg)
	// id + data
	m := make([]byte, 2+len(data[1]))
	// 默认使用大端序
	copy(m[0:], data[0])
	copy(m[2:], data[1])
	// 发送消息
	_ = wsconn.WriteMessage(websocket.BinaryMessage, m)
}

func main() {

	cliInfo := &ClientInfo{gameType: gameType}
	if len(os.Args) > 1 {
		gameName := os.Args[1]
		cliInfo.gameType = g.GetLabelCode(gameName)
	}

	wg := &sync.WaitGroup{}
	closeChan := make(chan struct{})
	if benchmark {
		initWSConns(wg, closeChan)
	} else {
		wsconn := initWSConn(cliInfo)
		defer wsconn.Close()
		go wsMsgHandler(closeChan, wg, wsconn)
	}

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, os.Kill)
	<-c
	fmt.Println("logout")
	close(closeChan)
	wg.Wait()
	fmt.Println("all done")
}
